package com.vitogiovanni.tobook;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Fragment;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentSender;
import android.location.Location;
import android.os.AsyncTask;
import android.os.AsyncTask.Status;
import android.os.Bundle;
import android.provider.Settings;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.Toast;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesClient;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.location.LocationClient;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.OnCameraChangeListener;
import com.google.android.gms.maps.GoogleMap.OnInfoWindowClickListener;
import com.google.android.gms.maps.MapView;
import com.google.android.gms.maps.MapsInitializer;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.VisibleRegion;
import com.google.api.client.extensions.android.http.AndroidHttp;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.vitogiovanni.tobook.entity.localeendpoint.Localeendpoint;
import com.vitogiovanni.tobook.entity.localeendpoint.model.CollectionResponseLocale;
import com.vitogiovanni.tobook.entity.localeendpoint.model.Locale;
import com.vitogiovanni.tobook.utilities.LocaleTaskCompleteListener;
import com.vitogiovanni.tobook.utilities.LocaliCaricatiListener;
import com.vitogiovanni.tobook.utilities.NetworkChecker;

/**
 * A placeholder fragment containing a simple view.
 */
public class MappaFragment extends Fragment implements LocaleTaskCompleteListener<List<Locale>>,
														GooglePlayServicesClient.ConnectionCallbacks,
														GooglePlayServicesClient.OnConnectionFailedListener,
														com.google.android.gms.location.LocationListener{
	
	private View rootView;
	private GoogleMap map;
	private MapView mapView;
	private Location location;
	private LocaleTask localeTask = null;
	private LocaliCaricatiListener<List<Locale>> callbackb;
	private double coordinataX;
	private double coordinataY;
	private double raggio;
	private List<Locale> locali = new ArrayList<Locale>();
	
	Bundle bundle;
	
	private List<String> servizi = new ArrayList<String>();
	private List<String> tipologie = new ArrayList<String>();
	
	private Map<Marker, String> allMarkersMap = new HashMap<Marker, String>();
	
	private final static int CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000;
	// Milliseconds per second
    private static final int MILLISECONDS_PER_SECOND = 1000;
    // Update frequency in seconds
    public static final int UPDATE_INTERVAL_IN_SECONDS = 5;
    // Update frequency in milliseconds
    private static final long UPDATE_INTERVAL =
            MILLISECONDS_PER_SECOND * UPDATE_INTERVAL_IN_SECONDS;
    // The fastest update frequency, in seconds
    private static final int FASTEST_INTERVAL_IN_SECONDS = 1;
    // A fast frequency ceiling in milliseconds
    private static final long FASTEST_INTERVAL =
            MILLISECONDS_PER_SECOND * FASTEST_INTERVAL_IN_SECONDS;
    
    //coordinates of center of Italy
    private static final double LATITUDE_CENTER_ITALY = 42.8333333;
    private static final double LONGITUDE_CENTER_ITALY = 12.833333333333334;
	
    //default zoom of showing location
    private static final float DEFAULT_ZOOM = 15;
    
    private LocationClient mLocationClient;
	private LocationRequest mLocationRequest;
	
	
	private boolean locationRetriveNeeded;
	
	/**
	 * The fragment argument representing the section number for this
	 * fragment.
	 */
	private static final String ARG_SECTION_NUMBER = "section_number";

	/**
	 * Returns a new instance of this fragment for the given section number.
	 */
	public static MappaFragment newInstance(int sectionNumber) {
		MappaFragment fragment = new MappaFragment();
		Bundle args = new Bundle();
		args.putInt(ARG_SECTION_NUMBER, sectionNumber);
		fragment.setArguments(args);
		return fragment;
	}

	public MappaFragment() {
	}

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		
		//View view = super.onCreateView(inflater, container, savedInstanceState);
		rootView = inflater.inflate(R.layout.fragment_mappa_locali,
				container, false);
		
		mapView = (MapView) rootView.findViewById(R.id.mapView);
        mapView.onCreate(savedInstanceState);
		
		Button buttonRicercaLocali = (Button) rootView.findViewById(R.id.button_ricerca_locali_in_mappa);
		
		buttonRicercaLocali.setOnClickListener(new OnClickListener() {
			
			public void onClick(View v) {
				
				Intent intent = new Intent(getActivity(), RicercaLocaliActivity.class);
				startActivity(intent);
			}
		});
        
		initializeMap();
		       
		return rootView;
	}
	
	@Override
	public void onActivityCreated(Bundle savedInstanceState){
		super.onActivityCreated(savedInstanceState);
		
		bundle = getActivity().getIntent().getExtras();
		locationRetriveNeeded= true;
		// Create the LocationRequest object
        mLocationRequest = LocationRequest.create();
        // Use high accuracy
        mLocationRequest.setPriority(
                LocationRequest.PRIORITY_LOW_POWER);
        // Set the update interval to 5 seconds
        mLocationRequest.setInterval(UPDATE_INTERVAL);
        // Set the fastest update interval to 1 second
        mLocationRequest.setFastestInterval(FASTEST_INTERVAL);
		
		
		if (!servicesConnected()) {
			getActivity().finish();
		}

	}
	
	public void getParams(){
        
		raggio = 0.5;
		if(location != null){
			coordinataX = location.getLongitude();
			coordinataY = location.getLatitude();
		}
		
		bundle = getActivity().getIntent().getExtras();
		if (bundle != null) {
			
			if (bundle.getStringArrayList("servizi") != null) {
				servizi = bundle.getStringArrayList("servizi");
			}
			else{
				servizi.add("no");
			}
			
			if (bundle.getStringArrayList("tipologie") != null) {
				tipologie = bundle.getStringArrayList("tipologie");
			}
			else
			{
				tipologie.add("no");
			}
				
			if (bundle.get("coordinataX") != null) {
				coordinataX = ((Number)bundle.get("coordinataX")).doubleValue();
				coordinataY = ((Number)bundle.get("coordinataY")).doubleValue();
				CameraPosition cameraPosition = new CameraPosition(new LatLng(
						coordinataY, coordinataX), DEFAULT_ZOOM, 0, 0);
				map.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));	
			}
		}
		else{
			servizi.add("no");
			tipologie.add("no");
		}
	}
	
	@Override
	public void onPause() {
	    super.onPause();
	  }
	
	@Override
	public void onResume() {
		super.onResume();
		
		if(locationRetriveNeeded)
		{
			locationRetriveNeeded = false;
			locationRetrieve();
		}
		super.onResume();

	}
	
	@Override
	public void onStop()  {
		if(mLocationClient != null){	
			mLocationClient.disconnect();
		}
		if(localeTask != null)
		{
			localeTask.cancel(true);
		}
		super.onStop();
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public void onAttach(Activity a){
		super.onAttach(a);
		callbackb = (LocaliCaricatiListener<List<Locale>>)a;
	}
	
	public void initializeMap(){
		
        mapView.onResume();
        
        map = mapView.getMap();
        map.getUiSettings().setMyLocationButtonEnabled(true);
        map.setMyLocationEnabled(true);

        map.setOnInfoWindowClickListener(new OnInfoWindowClickListener() {
			
			@Override
			public void onInfoWindowClick(Marker marker) {
				
				Intent intentLocale = new Intent(getActivity(), LocaleActivity.class);
				
				String idLocale = allMarkersMap.get(marker);
				String nomeLocale = marker.getTitle();
				intentLocale.putExtra("idLocale", idLocale);
				intentLocale.putExtra("nomeLocale", nomeLocale);
				if (!NetworkChecker.isInternetAvailable(getActivity().getApplicationContext())) {
					NetworkChecker.noConnectionToast(getActivity().getApplicationContext());
					return;
				}
				startActivity(intentLocale);
			}
		});
        
        MapsInitializer.initialize(getActivity().getApplicationContext());
        CameraPosition cameraPosition = new CameraPosition(new LatLng(LATITUDE_CENTER_ITALY, LONGITUDE_CENTER_ITALY), 5, 0, 0);
        map.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
	}
	/**
	 * Mostra la posizione dell'utente sulla mappa dopo aver definito
	 * alcuni criteri per la localizzazione.
	 */
	public void showMyPosition(){
		
		location = mLocationClient.getLastLocation();

        if(location!=null){
	        CameraPosition cameraPosition = new CameraPosition(new LatLng(location.getLatitude(), location.getLongitude()), DEFAULT_ZOOM, 0, 0);
	        map.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
        }
	}
	
	/**
	 * Mostra sulla mappa il marker (relativo ad esempio ad uno o pi� locali).
	 * @param latitude
	 * @param longitude
	 * @param name
	 */
	public Marker addMarkerOnMap(double latitude, double longitude, String name){
		
		MarkerOptions options = new MarkerOptions();
		options.position(new LatLng(latitude, longitude));
        options.title(name);
        options.icon(BitmapDescriptorFactory.fromResource(R.drawable.marker));
        Marker marker = map.addMarker(options);
        return marker;
	}
	
	/**
	 * Elabora la lista dei Locali ottenuta tramite l'AsyncTask
	 * e posiziona i locali sulla mappa.
	 */
	public void onTaskComplete(List<Locale> listLocali) {
		//this.listLocali = listLocali;
				
		List<String> listItems = new ArrayList<String>();
		if(listLocali!=null){
			if (listLocali.size() + locali.size() == 0){
				Toast.makeText(getActivity().getApplicationContext(), getResources().getString(R.string.messaggio_no_locali), Toast.LENGTH_SHORT).show();
			}
			else{	
				int i = 0;
				String[] distanzaLocale = new String[listLocali.size()];
				for (Locale loc : listLocali) {
					listItems.add(loc.getVia() + " " + loc.getCitta());
					
					Location locationDest = new Location("dest");
					locationDest.setLatitude(loc.getCoordinataY());
					locationDest.setLongitude(loc.getCoordinataX());
					if(location!=null)
					{
						float distance = location.distanceTo(locationDest)/1000;
						distanzaLocale[i] = String.format("%.2f", distance).replace(",", ".");
					}
					else
					{
						distanzaLocale[i] = "--";
					}
					Marker mark = addMarkerOnMap(loc.getCoordinataY(), loc.getCoordinataX(), loc.getNome());
					allMarkersMap.put(mark, loc.getId().toString());
					i++;
				}
				callbackb.caricaLocali(listLocali, distanzaLocale);
			}
		}
	}
	
	/**
	 * Chiama l'AsyncTask per poter prelevare la lista dei locali
	 * presenti nel Datastore.
	 */
	public void loadLocali(){
		if (!NetworkChecker.isInternetAvailable(getActivity())) {
			NetworkChecker.noConnectionToast(getActivity());
			return;
		}
		if (coordinataX != 0.0d && coordinataY != 0.0d) {
			if (localeTask == null) {
				localeTask = (LocaleTask) new LocaleTask(getActivity()
						.getApplicationContext(), this).execute();
			} else if (localeTask.getStatus().equals(Status.FINISHED)) {
				localeTask = (LocaleTask) new LocaleTask(getActivity()
						.getApplicationContext(), this).execute();
			}
		}
		else
		{
			Toast.makeText(this.getActivity().getApplicationContext(), 
					getResources().getString(R.string.rilevando_positione), Toast.LENGTH_SHORT).show();
		}
		
	}
	
	private void locationRetrieve(){
		String locationProviders = Settings.Secure.getString(getActivity()
				.getContentResolver(),
				Settings.Secure.LOCATION_PROVIDERS_ALLOWED);

		if ((locationProviders == null || locationProviders.equals("")) && (bundle ==null || bundle.get("coordinataX") == null)) {

			locationRetriveNeeded = true;
			new AlertDialog.Builder(getActivity())
					.setTitle("Attiva geolocalizzazione")
					.setMessage("Questa applicazione richiede l'utilizzo del servizio di Geolocalizzazione Google. "
									+ "Vuoi abilitare questo servizio?")
					.setPositiveButton("Si",
							new DialogInterface.OnClickListener() {
								public void onClick(DialogInterface dialog,
										int which) {
									// show system settings
									startActivity(new Intent(
											Settings.ACTION_LOCATION_SOURCE_SETTINGS));
								}
							})
					.setNegativeButton("No",
							new DialogInterface.OnClickListener() {
								public void onClick(DialogInterface dialog,
										int which) {
									getActivity().finish();
								}
							})
							.setCancelable(false)
							.show();
		} else {
			mLocationClient = new LocationClient(getActivity()
					.getApplicationContext(), this, this);
			mLocationClient.connect();
		}
	}

	private boolean servicesConnected() {
        // Check that Google Play services is available
        int resultCode =
                GooglePlayServicesUtil.isGooglePlayServicesAvailable(getActivity());
        // If Google Play services is available
        if (ConnectionResult.SUCCESS == resultCode) {
            return true;
        // Google Play services was not available for some reason
        }
        else {
        	return false;     
        }
    }
	
	/**
     * Called by Location Services when the request to connect the
     * client finishes successfully. At this point, you can
     * request the current location or start periodic updates
     */
    @Override
    public void onConnected(Bundle dataBundle) {
    	mLocationClient.requestLocationUpdates(mLocationRequest, this);
        showMyPosition();
        
        getParams();
    	
        if(location!=null){
	        CameraPosition cameraPosition = new CameraPosition(new LatLng(coordinataY, coordinataX), DEFAULT_ZOOM, 0, 0);
	        map.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
        }
    	loadLocali();
        
        map.setOnCameraChangeListener(new OnCameraChangeListener() {
			
			@Override
			public void onCameraChange(CameraPosition positionCamera) {

				coordinataX = map.getCameraPosition().target.longitude;
		    	coordinataY = map.getCameraPosition().target.latitude;
		    	Location center = new Location("center");
		    	center.setLatitude(coordinataY);
		    	center.setLongitude(coordinataX);
		    	
		    	VisibleRegion vr = map.getProjection().getVisibleRegion();
		    	Location dest = new Location("dest");
		    	dest.setLatitude(vr.latLngBounds.southwest.latitude);
		    	dest.setLongitude(coordinataX);

		    	raggio = center.distanceTo(dest)/1000;

		    	loadLocali();		
			}
		});
        
    }

    /**
     * Called by Location Services if the connection to the
     * location client drops because of an error.
     */
    @Override
    public void onDisconnected() {
        // Display the connection status
        Toast.makeText(getActivity().getApplicationContext(), getResources().getString(R.string.play_service_off),
                Toast.LENGTH_SHORT).show();
    }

    /**
     * Called by Location Services if the attempt to
     * Location Services fails.
     */
    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
        /*
         * Google Play services can resolve some errors it detects.
         * If the error has a resolution, try sending an Intent to
         * start a Google Play services activity that can resolve
         * error.
         */
        if (connectionResult.hasResolution()) {
            try {
                // Start an Activity that tries to resolve the error
                connectionResult.startResolutionForResult(
                        getActivity(), CONNECTION_FAILURE_RESOLUTION_REQUEST);
                /*
                 * Thrown if Google Play services canceled the original
                 * PendingIntent
                 */
            } catch (IntentSender.SendIntentException e) {
                // Log the error
                e.printStackTrace();
            }
        } else {
            /*
             * If no resolution is available, display a dialog to the
             * user with the error.
             */
            //showErrorDialog(connectionResult.getErrorCode());
        }
    }

    @Override
	public void onLocationChanged(Location location) {
    	this.location = location;
    	if(this.coordinataX == 0.0d && this.coordinataY == 0.0d)
    	{
    		coordinataX = location.getLongitude();
    		coordinataY = location.getLatitude();
    		if(location!=null){
    	        CameraPosition cameraPosition = new CameraPosition(new LatLng(coordinataY, coordinataX), DEFAULT_ZOOM, 0, 0);
    	        map.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
            }
    		loadLocali();
    	}
	}
	
	/**
	 * Task che si interfaccia con il Datastore.
	 * @author Giovanni
	 *
	 */
	private class LocaleTask extends AsyncTask<Void, Void, List<Locale>>
	{
		private LocaleTaskCompleteListener<List<Locale>> callback;
		private ProgressBar progressBar = (ProgressBar) rootView.findViewById(R.id.loading_locali_mappa);
		
		public LocaleTask(Context context, LocaleTaskCompleteListener<List<Locale>> cb)
		{
			this.callback = cb;
		}
		
		@Override
		protected void onPreExecute(){
			progressBar.setVisibility(View.VISIBLE);
		}
		
		@Override
		protected List<Locale> doInBackground(Void... arg0) {
			
			Localeendpoint.Builder builder1 = new Localeendpoint.Builder(
					AndroidHttp.newCompatibleTransport(), new JacksonFactory(),
					null);
			builder1 = CloudEndpointUtils.updateBuilder(builder1);
			builder1.setApplicationName("to-book");
			Localeendpoint localeEndpoint = builder1.build();
			List<Locale> localiNuovi = new ArrayList<Locale>();
			List<Locale> localiQuery = new ArrayList<Locale>();
			
			try{
				CollectionResponseLocale localiColl = localeEndpoint.getLocali(coordinataX, coordinataY, (double) raggio, servizi, tipologie).execute();
				localiQuery = localiColl.getItems();
				
				if (localiQuery != null) {
					if (locali.size() == 0) {
						localiNuovi.addAll(localiQuery);
					}
					else{
						for (Locale loc : localiQuery) 
						{
							boolean caricaLocale = false;
							for (Locale locB : locali) 
							{
								if (locB.getId().getId().equals(loc.getId().getId())) {
									caricaLocale = false;
									break;
								}else{
									caricaLocale = true;
								}
							}
							if(caricaLocale)
							{
								localiNuovi.add(loc);
							}
						}
					}			
					locali.addAll(localiNuovi);
				}
				
				
			}catch(IOException e){
				e.printStackTrace();
			}
			return localiNuovi;			
		}
		
		@Override
		protected void onPostExecute(List<Locale> result) {

			progressBar.setVisibility(View.GONE);
			callback.onTaskComplete(result);
		}	
	}    
}
